﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using warcaby.Backpropagation;
using warcaby.LearningRateFunctions;
using warcaby.Initializers;
using warcaby.Properties;
using warcaby.SOM;
using System.IO;
using System.Text.RegularExpressions;
namespace warcaby
{

    class TDLearning
    {
        int MinMaxwithoutAlphaBeta=0;
        int MinMaxwithAlphaBeta=0;

        private string FileName;
        private StreamReader streamReader;

        public BackpropagationNetwork network;
        public TDLearning()
        {
            LinearLayer inputLayer = new LinearLayer(34);
            
            SigmoidLayer hiddenLayer = new SigmoidLayer(8);
            SigmoidLayer outputLayer = new SigmoidLayer(1);
            new BackpropagationConnector(inputLayer, hiddenLayer);
            new BackpropagationConnector(hiddenLayer, outputLayer);
            network = new BackpropagationNetwork(inputLayer, outputLayer);

        }
        // Training function which returns MSE
        public string Training(int rank, int number_of_games, int epchos)
        {

            // Set learning rate
            //network.SetLearningRate(0.25);

            TrainingSet trainingSet = new TrainingSet(34, 1);

            dodajTreningi(trainingSet, @"draughts-games", rank,number_of_games);

            network.Learn(trainingSet, epchos);

            // We return MSE
            return network.MeanSquaredError.ToString("0.000000");
        }

        private void zamianaWartosciWPlanszy(double[,] inputRaw)
        {
            for (int x = 0; x < 8; x++)
            {
                for (int y = 0; y < 8; y++)
                {
                    if(inputRaw[y,x] == 1)
                    {
                        inputRaw[y, x] = -1;
                    }
                    else if (inputRaw[y, x] == -1)
                    {
                        inputRaw[y, x] = 1;
                    }
                }
            }
        }
        private void dodajTreningi(TrainingSet trainingSet, string fileName, int minRank, int maxNumberOfTrainingGames)
        {
            FileName = fileName;
            streamReader = new StreamReader(fileName);

            int currentNumberOfGames = 0;
            // Wynik:
            double[] wynik = new double[1];
            while(++currentNumberOfGames<=maxNumberOfTrainingGames)
            {
                bool stillWorking = true;

                bool bPierwszyGraczWygral = true;

                
                
                // Input:
                double[,] inputRaw = new double[8,8];

                // nadanie inputowi wartosci
                bool bChanger2 = false;
                bool bChanger = true;
                int noOfWhite= 0;
                int noOfBlack = 0;
                int noOfEmpty = 0;
                for (int x = 0; x < 8; x++ )
                {
                    for (int y = 0; y < 8; y++ )
                    {
                        if (bChanger2 && y == 0)
                        {
                            inputRaw[y, x] = -999;
                            bChanger2 = !bChanger2;
                        }
                        else
                        {
                            if (bChanger)
                            {
                                if (noOfWhite < 12)
                                {
                                    inputRaw[y, x] = 1;
                                    noOfWhite++;
                                }
                                else if (noOfEmpty < 8)
                                {
                                    inputRaw[y, x] = 0;
                                    noOfEmpty++;
                                }
                                else if (noOfBlack < 12)
                                {
                                    inputRaw[y, x] = -1;
                                    noOfBlack++;
                                }
                            }
                            if (!bChanger)
                            {
                                inputRaw[y, x] = -999;
                            }
                            bChanger = !bChanger;
                            if(y==7)
                            {
                                bChanger = !bChanger;
                            }
                        }
                    }
                }

                    while (stillWorking)
                    {
                        string s = ReadFileByLine();
                        if (s == null) return;



                        // Znajdujemy pierwsze [Event
                        /*while (!s.StartsWith("[Event"))
                        {
                            s = ReadFileByLine();
                        }*/
                        if (s.StartsWith("[Result"))
                        {
                            s = s.Remove(0, 9);
                            if (s.StartsWith("1"))
                            {
                                bPierwszyGraczWygral = true;
                                wynik[0] = 100;
                            }
                            else
                            {
                                bPierwszyGraczWygral = false;
                                zamianaWartosciWPlanszy(inputRaw);
                                wynik[0] = 100;
                            }
                        }
                        else if (s.StartsWith("[WhiteElo"))
                        {
                            s = s.Remove(0, 11);
                            if(s.Length == 6)
                            {
                                s = s.Remove(4);
                            }
                            else if (s.Length == 5)
                            {
                                s = s.Remove(3);
                            }
                            else if (s.Length == 4)
                            {
                                s = s.Remove(2);
                            }
                            else
                            {
                                s = s.Remove(1);
                            }
                            
                            int r = int.Parse(s);
                            if (r < minRank) // za maly ranking (wiekszy => lepszy)
                            {
                                stillWorking = false;
                                currentNumberOfGames--;
                            }
                        }
                        else if (s.StartsWith("[BlackElo"))
                        {
                            s = s.Remove(0, 11);
                            if (s.Length == 6)
                            {
                                s = s.Remove(4);
                            }
                            else if (s.Length == 5)
                            {
                                s = s.Remove(3);
                            }
                            else if (s.Length == 4)
                            {
                                s = s.Remove(2);
                            }
                            else
                            {
                                s = s.Remove(1);
                            }
                            int r = int.Parse(s);
                            if (r < minRank) // za maly ranking (wiekszy => lepszy)
                            {
                                stillWorking = false;
                                currentNumberOfGames--;
                            }
                        }
                        else if (s.StartsWith("1."))
                        {

                            bool bSwitcherOfPlayer = true;

                            if (!stillWorking) continue;
                            StringBuilder sb = new StringBuilder();
                            sb.Append(s);
                            string tmp;
                            while ((tmp = ReadFileByLine()) != "")
                            {
                                sb.Append(tmp + " ");
                            }
                            if (sb.ToString().Contains("{*}")) { stillWorking = false; continue; }
                            Regex rgx = new Regex("[0-9][.] | [0-9][0-9][.]");
                            int max_number_of_moves = 0;
                            foreach (string str_check in rgx.Split(sb.ToString()))
                            {
                                max_number_of_moves++;
                            }
                            int current_move = 0;
                            foreach (string str in rgx.Split(sb.ToString()))
                            {
                                current_move++;
                                bSwitcherOfPlayer = true; // true - pierwszy gracz  false - drugi gracz
                                string[] ss = str.Split(' ');
                                //if (ss[0] == "") ss.
                                //if (ss.Length != 4) continue;
                                for (int m = 0; m < ss.Length; m++)
                                {
                                    if (ss[m] == "")
                                    {
                                        continue;
                                    }
                                    if (ss[m][0] == '0' || ss[m][0] == '1')
                                    {
                                        stillWorking = false; break;
                                    }

                                    int k = 0;
                                    while (k + 2 < ss[m].Length && (ss[m][k + 2] == 'x' || ss[m][k + 2] == '-'))
                                    {
                                        char sdf = ss[m][k + 0];
                                        int no = getNoFromLetter(sdf);
                                        int no2 = int.Parse(ss[m].Substring(k + 1, 1)) - 1;
                                        //p.pole[no, no2] = 1;
                                        sdf = ss[m][k + 3];
                                        int no3 = getNoFromLetter(sdf);
                                        int no4 = int.Parse(ss[m].Substring(k + 4, 1)) - 1;

                                        // dodanie treningu
                                        double[] plansza_teraz = zwrocPolaPlanszy(inputRaw, no, no2, no3, no4, ss[m][k + 2]);
                                        if (bSwitcherOfPlayer == bPierwszyGraczWygral)// switchutjemy playerow - interesuja nas tylko wygrane
                                        {
                                            double[] reward = new double[1];
                                            reward[0] = /*(Convert.ToDouble(current_move) / Convert.ToDouble(max_number_of_moves)) * */Convert.ToDouble(wynik[0]);
                                            trainingSet.Add(new TrainingSample(plansza_teraz,reward));
                                        }
                                        else if(!bSwitcherOfPlayer == !bPierwszyGraczWygral)
                                        {
                                            double[] reward = new double[1];
                                            reward[0] = /*(current_move / max_number_of_moves) **/ wynik[0];
                                            trainingSet.Add(new TrainingSample(plansza_teraz, reward));
                                        }
                                        /*if (bSwitcherOfPlayer)
                                        {
                                            trainingSet.Add(new TrainingSample(plansza_teraz, wynik));
                                        }*/
                                        k = k + 3;
                                    
                                    }
                                    bSwitcherOfPlayer = !bSwitcherOfPlayer;
                                }
                                if (!stillWorking) break;
                            }
                            if (!stillWorking)
                            { continue; }
                        }

                    }
               


            }
        }

        

        private double[] zwrocPolaPlanszy(double[,] input, int no1, int no2, int no3, int no4, char typ_ruchu)
        {
            if(typ_ruchu == '-')
            {
                double temp = input[no1,no2];
                input[no1, no2] = input[no3, no4];
                input[no3, no4] = temp;
                
            }
            else // typ_ruchu == 'x' UWAGA ! Pytanie do siebie: Czy siec poprawnie wyznaczy że 2 bicia > 1 bicia ? -> raczej tak
            {
                double temp = input[no1, no2];
                input[no1, no2] = input[no3, no4];
                input[no3, no4] = temp;

                // wymazanie bitego pionka: // DOROBIC DAMKE
                //Math.Abs()
                if(no3 - no1 < 0 && no4 - no2 < 0) // bicie w dolny lewy róg 
                {
                    // damka
                    if(no3-no1 < -2)
                    {
                        for(int i = 1; i < no3-no1 ; i++)
                        {
                            input[no1 - i, no2 - i] = 0;
                        }
                    }
                    else
                    {
                        input[no1 - 1, no2 - 1] = 0;
                    }
                }
                else if (no3 - no1 < 0 && no4 - no2 > 0)// bicie w górny lewy róg 
                {
                    // damka
                    if (no3 - no1 < -2)
                    {
                        for (int i = 1; i < no3 - no1; i++)
                        {
                            input[no1 - i, no2 + i] = 0;
                        }
                    }
                    else
                    {
                        input[no1 - 1, no2 + 1] = 0;
                    }
                    
                }
                else if (no3 - no1 > 0 && no4 - no2 < 0)// bicie w dolny prawy róg 
                {
                    // damka
                    if (no3 - no1 > 2)
                    {
                        for (int i = 1; i < no3 - no1; i++)
                        {
                            input[no1 + i, no2 - i] = 0;
                        }
                    }
                    else
                    {
                        input[no1 + 1, no2 - 1] = 0;
                    }
                    
                }
                else if (no3 - no1 > 0 && no4 - no2 > 0)// bicie w górny prawy róg 
                {
                    // damka
                    if (no3 - no1 > 2)
                    {
                        for (int i = 1; i < no3 - no1; i++)
                        {
                            input[no1 + i, no2 + i] = 0;
                        }
                    }
                    else
                    {
                        input[no1 + 1, no2 + 1] = 0;
                    }
                    
                }
            }
            // Zamiana pionka na damke jeśli trzeba:
            if (input[no3, no4] == 1 && no4==7)
            {
                input[no3, no4] = 2;
            }
            if (input[no3, no4] == -1 && no4 == 0)
            {
                input[no3, no4] = -2;
            }

            return convertRawInputToInput(input);
        }

        private double[] convertRawInputToInput(double[,] inputRaw)
        {
            double[] input = new double[34];
            int i = 0;
            for (int x = 0; x < 8; x++)
            {
                for (int y = 0; y < 8; y++)
                {
                    if (inputRaw[y,x] != -999)
                    {
                        input[i++] = inputRaw[y,x];
                    }
                }
            }
            input[32] = GetBlack(inputRaw) - GetWhite(inputRaw);
            input[33] = GetBlackQueen(inputRaw) - GetWhiteQueen(inputRaw);
            return input;
        }

        private int GetWhiteQueen(double[,] inputRaw)
        {
            int white = 0;
            for (int x = 0; x < 8; x++)
                for (int y = 0; y < 8; y++)
                    if (inputRaw[x, y] == 2)
                        white++;
            return white;
        }

        private int GetBlackQueen(double[,] inputRaw)
        {
            int black = 0;
            for (int x = 0; x < 8; x++)
                for (int y = 0; y < 8; y++)
                    if (inputRaw[x, y] == -2)
                        black++;
            return black;
        }

        private int GetWhite(double[,] inputRaw)
        {
            int white = 0;
            for (int x = 0; x < 8; x++)
                for (int y = 0; y < 8; y++)
                    if (inputRaw[x, y] == 1 || inputRaw[x, y] == 2)
                        white++;
            return white;
        }

        private int GetBlack(double[,] inputRaw)
        {
            int black = 0;
            for (int x = 0; x < 8; x++)
                for (int y = 0; y < 8; y++)
                    if (inputRaw[x, y] == -1 || inputRaw[x, y] == -2)
                        black++;
            return black;
        }
        private int getNoFromLetter(char s)
        {
            switch (s)
            {
                case 'a': return 0;
                case 'b': return 1;
                case 'c': return 2;
                case 'd': return 3;
                case 'e': return 4;
                case 'f': return 5;
                case 'g': return 6;
                case 'h': return 7;
                default: return -1;
            }
        }
        private string ReadFileByLine()
        {
            string s = streamReader.ReadLine();
            return s;
        }
        public Stack<Move> TDmove(Map map, Stack<Move> Moves)
        {
            double bestvalue = -1111;
            Move M1 = new Move();
            int count = Moves.Count;
            Stack<Move> m = new Stack<Move>();
            while (Moves.Count != 0)
            {

                M1 = Moves.Pop();
                Map maptmp = map.CopyMap(map);
                if (maptmp.Move(M1) == -1)
                    Console.WriteLine("error");//debug
                double[] val1 = network.Run(maptmp.getArray());
                double val = val1[0];
                if (bestvalue == val)
                {
                    m.Push(M1);
                }
                if (map.Turn == "black")
                    if (bestvalue < val || bestvalue == -1111)
                    {
                        m.Clear();
                        m.Push(M1);
                        bestvalue = val;
                    }
                if (map.Turn == "white")
                    if (bestvalue > val || bestvalue == -1111)
                    {
                        m.Clear();
                        m.Push(M1);
                        bestvalue = val;
                    }


            }
            return m;
        }

    }
 
}
